using Avalonia.Platform;
using Making.Avalonia.WebView.Core.Abstractions;
using Making.Avalonia.WebView.Core.Models;
using Microsoft.Extensions.Logging;
using Microsoft.Web.WebView2.Core;
using Microsoft.Web.WebView2.WinForms;

namespace Making.Avalonia.WebView.Windows;

public class WindowsWebViewProvider : IWebViewProvider, IDisposable
{
    private readonly ILogger<WindowsWebViewProvider>? _logger;
    private readonly Dictionary<IntPtr, WebView2> _webViews = new();
    private readonly Dictionary<IntPtr, WebViewConfiguration> _configurations = new();
    private bool _disposed;

    public event EventHandler<WebResourceRequestedEventArgs>? WebResourceRequested;
    public event EventHandler<NavigationEventArgs>? NavigationStarting;
    public event EventHandler<NavigationEventArgs>? NavigationCompleted;

    public WindowsWebViewProvider(ILogger<WindowsWebViewProvider>? logger = null)
    {
        _logger = logger;
    }

    public IPlatformHandle CreateWebView(IPlatformHandle parent, WebViewConfiguration configuration)
    {
        if (!OperatingSystem.IsWindows())
            throw new PlatformNotSupportedException("Windows WebView provider only supports Windows platform");

        _logger?.LogDebug("Creating Windows WebView2 control");

        var webView2 = new WebView2();
        
        // Set user data folder if specified
        if (!string.IsNullOrEmpty(configuration.UserDataFolder))
        {
            webView2.CreationProperties = new CoreWebView2CreationProperties
            {
                UserDataFolder = configuration.UserDataFolder
            };
        }

        // Store configuration for later use
        _configurations[webView2.Handle] = configuration;
        _webViews[webView2.Handle] = webView2;

        // Subscribe to events
        webView2.CoreWebView2InitializationCompleted += OnCoreWebView2InitializationCompleted;
        
        // Initialize the WebView2 control
        _ = webView2.EnsureCoreWebView2Async();

        return new PlatformHandle(webView2.Handle, "HWND");
    }

    public void DestroyWebView(IPlatformHandle webView)
    {
        var handle = webView.Handle;
        
        if (_webViews.TryGetValue(handle, out var webView2))
        {
            _logger?.LogDebug("Destroying Windows WebView2 control");
            
            try
            {
                webView2.CoreWebView2InitializationCompleted -= OnCoreWebView2InitializationCompleted;
                
                if (webView2.CoreWebView2 != null)
                {
                    webView2.CoreWebView2.WebResourceRequested -= OnWebResourceRequested;
                    webView2.CoreWebView2.NavigationStarting -= OnNavigationStarting;
                    webView2.CoreWebView2.NavigationCompleted -= OnNavigationCompleted;
                }
                
                webView2.Dispose();
            }
            catch (Exception ex)
            {
                _logger?.LogWarning(ex, "Error disposing WebView2 control");
            }
            
            _webViews.Remove(handle);
            _configurations.Remove(handle);
        }
    }

    public async Task NavigateAsync(IPlatformHandle webView, Uri uri)
    {
        if (_webViews.TryGetValue(webView.Handle, out var webView2) && webView2.CoreWebView2 != null)
        {
            _logger?.LogDebug("Navigating to {Uri}", uri);
            webView2.CoreWebView2.Navigate(uri.ToString());
        }
    }

    public async Task<string> ExecuteScriptAsync(IPlatformHandle webView, string script)
    {
        if (_webViews.TryGetValue(webView.Handle, out var webView2) && webView2.CoreWebView2 != null)
        {
            _logger?.LogDebug("Executing script in WebView2");
            return await webView2.CoreWebView2.ExecuteScriptAsync(script);
        }
        
        return string.Empty;
    }

    public void SetZoomFactor(IPlatformHandle webView, double zoomFactor)
    {
        if (_webViews.TryGetValue(webView.Handle, out var webView2))
        {
            try
            {
                webView2.ZoomFactor = Math.Clamp(zoomFactor, 0.1, 4.0);
                _logger?.LogDebug("Set zoom factor to {ZoomFactor}", zoomFactor);
            }
            catch (Exception ex)
            {
                _logger?.LogWarning(ex, "Failed to set zoom factor");
            }
        }
    }

    public double GetZoomFactor(IPlatformHandle webView)
    {
        if (_webViews.TryGetValue(webView.Handle, out var webView2))
        {
            try
            {
                return webView2.ZoomFactor;
            }
            catch (Exception ex)
            {
                _logger?.LogWarning(ex, "Failed to get zoom factor");
            }
        }
        
        return 1.0;
    }

    public bool CanGoBack(IPlatformHandle webView)
    {
        return _webViews.TryGetValue(webView.Handle, out var webView2) 
               && webView2.CoreWebView2?.CanGoBack == true;
    }

    public bool CanGoForward(IPlatformHandle webView)
    {
        return _webViews.TryGetValue(webView.Handle, out var webView2) 
               && webView2.CoreWebView2?.CanGoForward == true;
    }

    public void GoBack(IPlatformHandle webView)
    {
        if (_webViews.TryGetValue(webView.Handle, out var webView2) && webView2.CoreWebView2?.CanGoBack == true)
        {
            webView2.CoreWebView2.GoBack();
            _logger?.LogDebug("WebView navigated back");
        }
    }

    public void GoForward(IPlatformHandle webView)
    {
        if (_webViews.TryGetValue(webView.Handle, out var webView2) && webView2.CoreWebView2?.CanGoForward == true)
        {
            webView2.CoreWebView2.GoForward();
            _logger?.LogDebug("WebView navigated forward");
        }
    }

    public void Reload(IPlatformHandle webView)
    {
        if (_webViews.TryGetValue(webView.Handle, out var webView2) && webView2.CoreWebView2 != null)
        {
            webView2.CoreWebView2.Reload();
            _logger?.LogDebug("WebView reloaded");
        }
    }

    public void AddWebResourceFilter(IPlatformHandle webView, string filter)
    {
        if (_webViews.TryGetValue(webView.Handle, out var webView2) && webView2.CoreWebView2 != null)
        {
            webView2.CoreWebView2.AddWebResourceRequestedFilter(filter, CoreWebView2WebResourceContext.All);
            _logger?.LogDebug("Added web resource filter: {Filter}", filter);
        }
    }

    private void OnCoreWebView2InitializationCompleted(object? sender, CoreWebView2InitializationCompletedEventArgs e)
    {
        if (e.IsSuccess && sender is WebView2 webView2 && webView2.CoreWebView2 != null)
        {
            _logger?.LogDebug("WebView2 core initialization completed successfully");
            
            // Get configuration for this webview
            if (_configurations.TryGetValue(webView2.Handle, out var config))
            {
                // Set initial zoom factor
                try
                {
                    webView2.ZoomFactor = Math.Clamp(config.InitialZoomFactor, 0.1, 4.0);
                }
                catch (Exception ex)
                {
                    _logger?.LogWarning(ex, "Failed to set initial zoom factor");
                }

                // Navigate to initial source if set
                if (config.InitialSource != null)
                {
                    webView2.CoreWebView2.Navigate(config.InitialSource.ToString());
                }
            }

            // Subscribe to events
            webView2.CoreWebView2.WebResourceRequested += OnWebResourceRequested;
            webView2.CoreWebView2.NavigationStarting += OnNavigationStarting;
            webView2.CoreWebView2.NavigationCompleted += OnNavigationCompleted;
        }
        else
        {
            _logger?.LogError("WebView2 core initialization failed: {Error}", e.InitializationException?.Message);
        }
    }

    private void OnWebResourceRequested(object? sender, CoreWebView2WebResourceRequestedEventArgs e)
    {
        var args = new WebResourceRequestedEventArgs(
            e.Request.Uri,
            e.Request.Method,
            e.Request.Headers.ToDictionary(h => h.Key, h => h.Value)
        );

        WebResourceRequested?.Invoke(this, args);

        if (args.Response != null && sender is CoreWebView2 coreWebView2)
        {
            var response = coreWebView2.Environment.CreateWebResourceResponse(
                args.Response.Content,
                args.Response.StatusCode,
                args.Response.StatusText,
                string.Join("\r\n", args.Response.Headers.Select(h => $"{h.Key}: {h.Value}"))
            );
            
            e.Response = response;
        }
    }

    private void OnNavigationStarting(object? sender, CoreWebView2NavigationStartingEventArgs e)
    {
        var args = new NavigationEventArgs(new Uri(e.Uri), e.IsUserInitiated, e.IsRedirected);
        NavigationStarting?.Invoke(this, args);
        e.Cancel = args.Cancel;
    }

    private void OnNavigationCompleted(object? sender, CoreWebView2NavigationCompletedEventArgs e)
    {
        if (sender is CoreWebView2 coreWebView2)
        {
            var args = new NavigationEventArgs(new Uri(coreWebView2.Source));
            NavigationCompleted?.Invoke(this, args);
        }
    }

    public void Dispose()
    {
        if (!_disposed)
        {
            foreach (var kvp in _webViews.ToList())
            {
                DestroyWebView(new PlatformHandle(kvp.Key, "HWND"));
            }
            
            _disposed = true;
        }
    }
}